﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Xceed.Silverlight.Compatibility;
using Xceed.Silverlight.Utils;

namespace Xceed.Silverlight.DataGrid
{
  internal class RootHost : ColumnElementsHost, IVisitorElement, IColumnElementHost, IXceedRoutedEventElement
  {
    internal static readonly double RootHostInitialOffset = 20d;    
    internal static readonly XceedRoutedEvent RootKeyDownEvent = new XceedRoutedEvent();

    public RootHost()
    {
      m_eventHelper = new XceedEventHelper( this );
      m_rootContainer = new VirtualRootContainer();
      m_childrenManager = new ChildrenManager<ILayoutElement>( this.AddChildren, this.RemoveChildren );
      this.KeyDown += new KeyEventHandler( this.OnKeyDown );
    }

    internal XceedEventHelper EventManager
    {
      get { return m_eventHelper; }
    }

    private IManageRootMeasure MeasureManager
    {
      get
      {
        return ( IManageRootMeasure )this.GetElementOrParentBase<IManageRootMeasure>();
      }
    }

    internal ChildrenManager<ILayoutElement> ChildrenManager
    {
      get
      {
        return m_childrenManager;
      }
    }

    internal VirtualRootContainer RootContainer
    {
      get
      {
        return m_rootContainer;
      }
    }

    internal TransitionContext PendingTransitionContext
    {
      get;
      set;
    }

    public IEnumerable<ColumnElementWrapper> ColumnElements
    {
      get
      {

        return CompatibilityUtils.GetEnumerable( this.Children )
          .Where( ( e ) => e is ColumnBackgroundControlWrapper )
          .Cast<ColumnElementWrapper>();
      }
    }

    protected override Size MeasureOverride( Size availableSize )
    {
      base.MeasureOverride( availableSize );
      Size measuredSize = this.MeasureManager.OnMeasureOverride( availableSize );
      measuredSize.Height = Math.Min( measuredSize.Height, availableSize.Height );
      return measuredSize;
    }

    protected override Size ArrangeOverride( Size finalSize )
    {
      base.ArrangeOverride( finalSize );
      Rect arrangeRect = new Rect( new Point( 0, 0 ), finalSize );
      Size arrangeSize = m_rootContainer.Arrange( new ArrangeParameters( arrangeRect, arrangeRect, new LayoutParameters( this.PendingTransitionContext ) ) );
      this.PendingTransitionContext = null;
      return arrangeSize;
    }

    internal Size DoMeasure( Size availableSize )
    {
      m_rootContainer.Measure( availableSize );
      return m_rootContainer.DesiredSize;
    }

    private void AddChildren( ILayoutElement element )
    {
      if( element is UIElement )
      {
        this.Children.Add( ( UIElement )element );
      }
    }

    private void RemoveChildren( ILayoutElement element )
    {
      if( element is UIElement )
      {
        this.Children.Remove( ( UIElement )element );
      }
    }

    internal bool TryGetElement( object key, out ILayoutElement element )
    {
      bool found = this.ChildrenManager.TryGetElement( key, out element );
      Debug.Assert( !found || !( ( IRecyclable )element ).IsRecycled );
      return found;
    }

    internal bool TryGetRecycledElement( object key, object exceptionKey, out ILayoutElement element )
    {
      bool found = this.ChildrenManager.TryGetRecycledElement( key, exceptionKey, out element );
      return found;
    }

    internal void AddElement( ILayoutElement element )
    {
      this.ApplyToHeadersFooters( element, 
        ( e ) => 
          {
            UIElement uiElement = (UIElement)e;
            if(!this.Children.Contains(uiElement))
            {
              this.Children.Add( uiElement );
            }
          }
          );
      this.ChildrenManager.Add( element );
    }

    internal void ReuseElement( object newPath, ILayoutElement wrapper )
    {
      this.ChildrenManager.ReuseElement( newPath, wrapper );
    }

    internal void RemoveElement( ILayoutElement element )
    {
      this.ApplyToHeadersFooters( element, ( e ) => this.Children.Remove( ( UIElement )e ) );
      this.ChildrenManager.Remove( element );

    }

    internal void RecycleElement( ILayoutElement element )
    {
      this.ApplyToHeadersFooters( element, ( e ) => e.LastArrangeRect = null );
      element.LastArrangeRect = null;
      this.ChildrenManager.Recycle( element );
    }

    private void ApplyToHeadersFooters( ILayoutElement element, Action<ILayoutElement> action )
    {
      if( element is VirtualContainerBase )
      {
        VirtualContainerBase container = ( VirtualContainerBase )element;
        foreach( ILayoutElement header in container.Headers )
        {
          action( header );
        }

        foreach( ILayoutElement footer in container.Footers )
        {
          action( footer );
        }
      }
    }

    private void OnKeyDown( object sender, KeyEventArgs e )
    {
      XceedEventUtils.RaiseKeyEvent( this, e, RootHost.RootKeyDownEvent );
    }

    internal void Accept( IVisitor visitor )
    {
      visitor.Visit( this );
    }

    private XceedEventHelper m_eventHelper;
    private VirtualRootContainer m_rootContainer;
    private ChildrenManager<ILayoutElement> m_childrenManager;

    #region IVisitorElement Members

    void IVisitorElement.Accept( IVisitor visitor )
    {
      throw new NotImplementedException();
    }

    #endregion

    #region IColumnElementHost Members

    IEnumerable<ColumnElementWrapper> IColumnElementHost.Elements
    {
      get { return this.ColumnElements; }
    }

    void IColumnElementHost.AddColumnElement( ColumnElementWrapper element )
    {
      Debug.Assert( element is ColumnBackgroundControlWrapper );
      this.AddColumnElement( element );
    }

    void IColumnElementHost.RemoveColumnElement( ColumnElementWrapper element )
    {
      Debug.Assert( element is ColumnBackgroundControlWrapper );
      this.RemoveColumnElement( element );
    }

    void IColumnElementHost.SetElementPosition( ColumnElementWrapper element, ColumnPosition position )
    {
      Debug.Assert( element is ColumnBackgroundControlWrapper );
      this.SetElementPosition( element, position );
    }

    void IColumnElementHost.InvalidateColumnArrange()
    {
      this.InvalidateColumnArrange();
    }

    void IColumnElementHost.InvalidateColumnMeasure()
    {
      this.InvalidateColumnMeasure();
    }

    #endregion

    #region IXceedRoutedEventElement interface (internal)

    XceedEventHelper IXceedRoutedEventElement.EventManager
    {
      get { return this.EventManager; }
    }

    #endregion

  }
}
